#include "LecteurPhraseAvecArbre.h"

#include <stdlib.h>
#include <iostream>
using namespace std;

////////////////////////////////////////////////////////////////////////////////

LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
ls(nomFich), ts() {
}


////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::analyse() {
    arbre = programme();
    cout << "Syntaxe correcte." << endl;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::programme() {
    // <programme> ::= debut <seq_inst> fin FIN_FICHIER
    fichierTrad.open("pgada.adb", ios_base::out);
    sauterSymCour("debut");
    fichierTrad << "begin" << endl;
    Noeud* si = seqInst();
    sauterSymCour("fin");
    fichierTrad << "end pgada;" << endl;
    testerSymCour("<FINDEFICHIER>");
    fichierTrad.close();
    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::seqInst() {
    // <seqInst> ::= <inst> ; { <inst> ; }

    NoeudSeqInst* si = new NoeudSeqInst();
    do {
        si->ajouteInstruction(inst());
        sauterSymCour(";");
        fichierTrad << ";" << endl;
    } while ((ls.getSymCour() == "<VARIABLE>") | (ls.getSymCour() == "si") | (ls.getSymCour() == "tantque")
            | (ls.getSymCour() == "repeter") | (ls.getSymCour() == "pour") | (ls.getSymCour() == "lire")
            | (ls.getSymCour() == "ecrire") | (ls.getSymCour() == "switch"));
    // tant que le symbole courant est un debut possible d'instruction...
    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::inst() {
    // <inst> ::= <affectation> | <instSi> | <instTq> | <instRepeter> 

    try {
        if (ls.getSymCour() == "si") return instSi();
        else if (ls.getSymCour() == "tantque") return instTq();
        else if (ls.getSymCour() == "repeter") return instRepeter();
        else if (ls.getSymCour() == "pour") return instPour();
        else if (ls.getSymCour() == "switch") return instSwitch();
        else if (ls.getSymCour() == "lire") return lire();
        else if (ls.getSymCour() == "ecrire") return ecrire();
        else return affectation();
    } catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instSi() {
    // <instSi> ::= si ( <expBool> ) <seqInst> { sinonsi ( <expBool> ) <seqInst> } [ sinon <seqInst> ] finsi

    Noeud* expB = NULL;
    Noeud* seqIn = NULL;
    Noeud* suivant = NULL;
    NoeudInstSi* instS = NULL;

    if (ls.getSymCour() == "si") {
        try {
            sauterSymCour("si");
            sauterSymCour("(");
            fichierTrad << "if (";
            expB = expBool();
            sauterSymCour(")");
            fichierTrad << ") then" << endl;
            seqIn = seqInst();
            suivant = instSi();
            instS = new NoeudInstSi(expB, seqIn, suivant);
            sauterSymCour("finsi");
            fichierTrad << "endif" << endl;
        } catch (char const*) {
            throw;
        }
    } else if (ls.getSymCour() == "sinonsi") {
        try {
            sauterSymCour("sinonsi");
            sauterSymCour("(");
            fichierTrad << "elsif (";
            expB = expBool();
            fichierTrad << (") then") << endl;
            sauterSymCour(")");
            seqIn = seqInst();
            suivant = instSi();
            instS = new NoeudInstSi(expB, seqIn, suivant);
        } catch (char const*) {
            throw;
        }
    } else if (ls.getSymCour() == "sinon") {
        try {
            sauterSymCour("sinon");
            fichierTrad << "else" << endl;
            seqIn = seqInst();
            expB = ts.chercheAjoute(Symbole("1"));
            instS = new NoeudInstSi(expB, seqIn, suivant);
        } catch (char const*) {
            throw;
        }
    }

    return instS;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instTq() {
    // <instTq> ::= tantque ( <expBool> ) <seqInst> fintantque

    try {
        sauterSymCour("tantque");
        fichierTrad << " while ";
        sauterSymCour("(");
        fichierTrad << "(";
        Noeud* exp = expBool();
        sauterSymCour(")");
        fichierTrad << ") loop" << endl;
        Noeud* seqinst = seqInst();
        sauterSymCour("fintantque");
        fichierTrad << " end loop";
        return new NoeudInstTantQue(exp, seqinst);
    } catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instRepeter() {
    // <instRepeter> ::= repeter <seqInst> jusqua ( <expBool> )

    try {
        sauterSymCour("repeter");
        Noeud* seq = seqInst();
        sauterSymCour("jusqua");
        sauterSymCour("(");
        Noeud* exp = expBool();
        sauterSymCour(")");

        return new NoeudInstRepeter(exp, seq);
    } catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instPour() {
    // <instPour> :: = pour ( <affectation>; <expBool>; <affectation ) <seqInst> finpour  
    Noeud* affect1;
    Noeud* expbool;
    Noeud* affect2;
    Noeud* seqinst;
    try {

        sauterSymCour("pour");
        fichierTrad << "for ";
        sauterSymCour("(");
        fichierTrad << ls.getSymCour().getChaine() << " ";
        fichierTrad.close();
        affect1 = affectation();
        sauterSymCour(";");
        expbool = expBool();
        sauterSymCour(";");
        affect2 = affectation();
        fichierTrad.open("pgada.adb", ios_base::in | ios_base::out);
        fichierTrad.seekp(ios::end);
        int b = ((ValeurInt*) affect1)->getValeur();
        fichierTrad << "in " << b << "..";
        while (((ValeurInt*) expbool)->getValeur() == 1) b++;
        fichierTrad << b;
        sauterSymCour(")");
        fichierTrad << " loop" << endl;

        seqinst = seqInst();
        sauterSymCour("finpour");
        fichierTrad << "end loop";

        return new NoeudInstPour(affect1, expbool, affect2, seqinst);
    } catch (char const*) {
        throw;
    }
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instSwitch() {
    // <instSwitch> :: = switch (<variable>) {case : <entier> <seqInst> } [ default : <seqInst> ] finswitch
    try {
        sauterSymCour("switch");
        fichierTrad << " case ";
        sauterSymCour("(");
        Noeud* variable = ts.chercheAjoute(ls.getSymCour());
        fichierTrad << ls.getSymCour().getChaine();

        ls.suivant();
        sauterSymCour(")");
        fichierTrad << " is " << endl;
        NoeudInstSwitch* nswitch = new NoeudInstSwitch(variable);
        do {
            sauterSymCour("case");
            fichierTrad << " when ";
            Noeud* cas = ts.chercheAjoute(ls.getSymCour());
            fichierTrad << ls.getSymCour().getChaine();
            ls.suivant();
            sauterSymCour(":");
            fichierTrad << " => " << endl;
            nswitch->ajouteCase(cas, seqInst());
        } while (ls.getSymCour() == "case");

        if (ls.getSymCour() == "defaut") {
            sauterSymCour("defaut");
            fichierTrad << " when others => " << endl;
            sauterSymCour(":");
            nswitch->ajouteCase(variable, seqInst());
        }
        sauterSymCour("finswitch");
        fichierTrad << "end case ";
        return nswitch;
    } catch (char const*) {
        throw "Switch";
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::affectation() {
    // <affectation> ::= <variable> = <expression>

    try {
        testerSymCour("<VARIABLE>");
        Noeud* var = ts.chercheAjoute(ls.getSymCour());
        fichierTrad << ls.getSymCour().getChaine();
        ls.suivant();
        sauterSymCour("=");
        fichierTrad << ":=";
        Noeud* exp = expression();
        return new NoeudAffectation(var, exp);
    } catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::lire() {
    // <lire> ::= lire ( <variable> )
    try {
        Noeud* fact = NULL;
        sauterSymCour("lire");
        sauterSymCour("(");
        fichierTrad << "ecrire(";
        if (ls.getSymCour() == "<VARIABLE>") {
            fact = facteur();
            sauterSymCour(")");
            fichierTrad << ")" << endl;
        } else
            erreur("<variable>");
        return new NoeudInstLire(fact);
    } catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::ecrire() {
    // <ecrire> ::= ecrire ( <expression> | <chaine> )
    try {
        sauterSymCour("ecrire");
        sauterSymCour("(");
        fichierTrad << "ecrire(";
        Noeud* phrase = expression();
        sauterSymCour(")");
        fichierTrad << ")";
        return new NoeudInstEcrire(phrase);
    } catch (char const*) {
        throw;
    }
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expression() {
    // <expression> ::= <expressionArit> | (<expBool>) | <CHAINE>

    Noeud* exp;
    try {
        if (ls.getSymCour() == "(") {
            ls.suivant();
            exp = expBool();
            sauterSymCour(")");
        } else if (ls.getSymCour() == "<CHAINE>" || ls.getSymCour() == "<CHAR>") {
            exp = ts.chercheAjoute(ls.getSymCour());
            if (ls.getSymCour() == "<CHAINE>" ) fichierTrad << ls.getSymCour().getChaine();
            else fichierTrad << ls.getSymCour().getChaine();
            ls.suivant();
        } else exp = expArit();
        return exp;
    } catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expArit() {
    // <expressionArit> ::= <terme> { <opAdd> <terme> }
    try {
        Noeud* term = terme();
        while (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
            Symbole opA = opAdd();
            Noeud* termDroit = terme();
            term = new NoeudOperateurBinaire(opA, term, termDroit);
        }
        return term;
    } catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::terme() {
    // <terme> ::= <facteur> { <opMult> <facteur> }
    try {
        Noeud* fact = facteur();
        while (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
            Symbole operateur = opMult();
            Noeud* factDroit = facteur();
            fact = new NoeudOperateurBinaire(operateur, fact, factDroit);
        }
        return fact;
    } catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::facteur() {
    // <facteur> ::= [<opUnaire>] <entier> | [<opUnaire>] <reel> | [<opUnaire>] <variable> 

    Noeud* fact = NULL;
    try {
        if (ls.getSymCour() == "<VARIABLE>" || ls.getSymCour() == "<ENTIER>" || ls.getSymCour() == "<REEL>") {
            fact = ts.chercheAjoute(ls.getSymCour());
            fichierTrad << ls.getSymCour().getChaine();
            ls.suivant();
        } else if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {
            Symbole s = ls.getSymCour();
            if (s == "-") fichierTrad << "-";
            else fichierTrad << "not ";
            ls.suivant();
            fact = new NoeudOperateurUnaire(s, facteur());
        } else erreur("<facteur>");

        return fact;
    } catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expBool() {
    // <expBool> ::= <relation> { <ou> <relation> }
    Noeud* rel = relation();
    try {
        while (ls.getSymCour() == "ou") {
            Symbole operateur = ls.getSymCour();
            fichierTrad << " or ";
            ls.suivant();
            Noeud* relDroit = relation();
            rel = new NoeudOperateurBinaire(operateur, rel, relDroit);
        }
        return rel;
    } catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::relation() {
    // <relation> ::= <condition> { <et> <condition> }
    try {
        Noeud* cond = condition();
        while (ls.getSymCour() == "et") {
            Symbole operateur = ls.getSymCour();
            fichierTrad << " and ";
            ls.suivant();
            Noeud* condDroit = relation();
            cond = new NoeudOperateurBinaire(operateur, cond, condDroit);
        }
        return cond;
    } catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::condition() {
    // <condition> ::= <expression> { <opRel> <expression> }

    Noeud* exp = expression();
    try {
        while (ls.getSymCour() == "==" || ls.getSymCour() == "!=" || ls.getSymCour() == "<" ||
                ls.getSymCour() == "<=" || ls.getSymCour() == ">" || ls.getSymCour() == ">=") {
            Symbole operateur = opRel();
            Noeud* expDroit = expression();
            exp = new NoeudOperateurBinaire(operateur, exp, expDroit);
        }
        return exp;
    } catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opAdd() {
    // <opAdd> ::= + | -

    Symbole opAdd;
    try {
        if (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
            opAdd = ls.getSymCour();
            fichierTrad << ls.getSymCour().getChaine();
            ls.suivant();
        } else
            erreur("<opAdd>");
        return opAdd;
    } catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opMult() {
    // <opMult> ::= * | /

    Symbole opMult;
    try {
        if (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
            opMult = ls.getSymCour();
            fichierTrad << ls.getSymCour().getChaine();
            ls.suivant();
        } else
            erreur("<opMult>");
        return opMult;
    } catch (char const*) {
        throw;
    }

}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opRel() {
    // <opRel> ::= == | != | < | <= | > | >=

    Symbole opRel;
    try {
        if (ls.getSymCour() == "==" || ls.getSymCour() == "!=" || ls.getSymCour() == "<" ||
                ls.getSymCour() == "<=" || ls.getSymCour() == ">" || ls.getSymCour() == ">=") {
            opRel = ls.getSymCour();
            if (ls.getSymCour() == "==") {
                fichierTrad << "=";
            } else if (ls.getSymCour() == "!=") {
                fichierTrad << "/=";
            } else {
                fichierTrad << ls.getSymCour().getChaine();
            }
            ls.suivant();
        } else
            erreur("<opRel>");
        return opRel;
    } catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opUnaire() {
    // <opUnaire> ::= - | non

    Symbole opUn;
    try {
        if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {
            opUn = ls.getSymCour();
            ls.suivant();
        } else
            erreur("<opUnaire>");
        return opUn;
    } catch (char const*) {
        throw;
    }
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::testerSymCour(string ch) {
    if (ls.getSymCour() != ch) {
        cout << endl << "-------- Erreur ligne " << ls.getLigne()
                << " - Colonne " << ls.getColonne() << endl << "   Attendu : "
                << ch << endl << "   Trouve  : " << ls.getSymCour() << endl
                << endl;
        exit(0); // plus tard, on levera une exception
    }
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
    testerSymCour(ch);
    ls.suivant();
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::erreur(string mess) {
    cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
            << ls.getColonne() << endl << "   Attendu : " << mess << endl
            << "   Trouve  : " << ls.getSymCour() << endl << endl;
    exit(0); // plus tard, on levera une exception
}
